#include "SegmentOracle.h"

namespace abliefern {
	
	SegmentOracle::SegmentOracle(KlausImage* theKlausImage, std::string theInputPathName, std::string theSvgPathName, std::string theOutputPathName, std::string theOutPutSuffix){
		_myIsFinishedCompletely = false;
				
		_myLineThreshold = 100;
		_myLineThresholdSquared = _myLineThreshold * _myLineThreshold;
		
		_myBorderThreshold = 10;
		
		_myMaxSegments = 40;
		
		_myKlausImage = theKlausImage;
		
		_myInputPathName = theInputPathName;
		_mySvgPathName = theSvgPathName;
		_myOutputPathName = theOutputPathName;
		
		_myOutputSuffix = theOutPutSuffix;
		
		
		_myOriginalImage.setUseTexture(false);
		_myCvImage.setUseTexture(false);

		
		
		resetCounts();
		resetBounds();
		
		
		_myPaths.clear();

		
		
		
	}
	
	
	void SegmentOracle::saveSegment(std::string theBaseFilename, std::string theFileName) {
		int ROI_BORDER = 16;
		
		int myRoiX = MAX(0, floor(_myMinX) - ROI_BORDER);
		int myRoiY = MAX(0, floor(_myMinY) - ROI_BORDER);
		
		int myRoiWidth  = MIN(_myOriginalImage.width - myRoiX, ceil(_myMaxX) - myRoiX + ROI_BORDER); 
		int myRoiHeight  = MIN(_myOriginalImage.height - myRoiY, ceil(_myMaxY) - myRoiY + ROI_BORDER); 
		
		
		
		/* Evaluate the border condition count */
		float myBorderPercentage = (float)_myBorderLength / (float)_myPathLength;
		
		
		
		
		
		bool myDoSave = false;
		
		for(int i = 0; i < _myVPs.size(); i++){
			
			
			VanishingPoint* myVP = &_myVPs.at(i);
			
			float mySupportFactor = (float)myVP->supportLength / (float)_myPathLength;
			
			if(mySupportFactor > 0.13){
				
				/* Store the segment */
				ImageSegment mySegment(theBaseFilename);
				
#ifdef USE_TIFFS
				mySegment.width = myRoiWidth;
				mySegment.height = myRoiHeight;
				
				mySegment.x = myRoiX  - myVP->x;
				mySegment.y = myRoiY - myVP->y;
#else
				mySegment.width = myRoiWidth * 2;
				mySegment.height = myRoiHeight * 2;
				
				mySegment.x = myRoiX * 2 - myVP->x;
				mySegment.y = myRoiY * 2 - myVP->y;		
				
#endif
				
				
				
				/* Use center points */
				mySegment.x += mySegment.width * 0.5;
				mySegment.y += mySegment.height * 0.5;
				
				mySegment.vanishingPoint = _myVPs.at(i);
				
				
				/* Set border percentage count */
				mySegment.borderPercentage = myBorderPercentage;
				
				
				
				_mySegments.push_back(mySegment);
				
				
				myDoSave = true;
				
				std::cout << "#### Saving segmentmask - Border percentage: " << myBorderPercentage << "\n";

				
			} else {
				std::cout << "--- Not enough support " << mySupportFactor << "\n";
				
			}
			
			
			
		}
		
		
		if(myDoSave){
		
		
		
		_myCvImage.setROI(myRoiX,myRoiY, myRoiWidth, myRoiHeight);
		IplImage* myFullImage = cvCreateImage(cvGetSize(_myCvImage.getCvImage()), IPL_DEPTH_8U, 4);
		
		
		cvCvtColor(_myCvImage.getCvImage(), myFullImage, CV_RGB2RGBA);
		
		
		/* Create grayscale mask image */
		IplImage* myMaskImage = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		cvSetZero(myMaskImage);
		//cvSet(myMaskImage, CV_RGB(0,0,255));
		
		
		/* Test drawing */
		
		CvPoint** myPaths = NULL;
		myPaths = new CvPoint*[_myPaths.size()]; 
		
		int myPathCount = _myPaths.size();
		
		int* myPathPointCount = NULL;
		myPathPointCount = new int[_myPaths.size()];
		
		
		for(int i = 0; i < _myPaths.size(); i++){
			
			PolygonPath* myPath = &(_myPaths.at(i));
			
			CvPoint* myPoints = NULL;
			myPoints = new CvPoint[myPath->size()];
			
			for(int j = 0; j < myPath->size(); j++){
				Point2f* myPoint = myPath->at(j);
				myPoints[j] = cvPoint(round(myPoint->x - myRoiX), round(myPoint->y - myRoiY));
			}
			
			myPathPointCount[i] = myPath->size();
			myPaths[i] = myPoints;
			
			
			
		}
		
		//cvFree_(pts);
		
		//cvFillConvexPoly(myMaskImage, pts, 3, CV_RGB(0,0,255), CV_AA, 0);
		
		//CvPoint 
		
		
		// use:
		
		//cvFillPoly(myMaskImage, pts, 3, CV_RGB(0,0,0), CV_AA, 0); // !!! needs an array of CVPoint !!!!
		
		
		cvFillPoly(myMaskImage, myPaths, myPathPointCount, myPathCount, CV_RGB(0,0,255), CV_AA, 0);
		
		//free(pts);
		
		
		/* Copy the mask image over to the alpha channel */
		cvSetImageCOI(myFullImage, 4); // alpha channel
		cvCopy(myMaskImage, myFullImage);
		
		
		
		/* Create openframeworks image for saving, coz de cv don't handle alpha */
		ofImage mySaveImage;
		mySaveImage.setUseTexture(false);
		mySaveImage.setFromPixels((unsigned char*)myFullImage->imageData, myFullImage->width, myFullImage->height, OF_IMAGE_COLOR_ALPHA, TRUE);
		
		std::cout << "### Saving segment " << theFileName << "\n";
		mySaveImage.saveImage(theFileName);
		
		
		/* Clean up */
		delete [] myPathPointCount;
		
		for(int i = 0; i < myPathCount; i++){
			delete [] myPaths[i];
		}
		
		delete [] myPaths;
		
		_myCvImage.resetROI();
		cvReleaseImage(&myMaskImage);
		cvReleaseImage(&myFullImage);
		
		

		
		}

		
		
		
	}
	
	
	std::string ReplaceString(const std::string &stringSearchString, const std::string &stringReplaceString, std::string stringStringToReplace)
	{ 
        std::string::size_type pos = stringStringToReplace.find(stringSearchString, 0);
        int intLengthSearch = stringSearchString.length();
		
        while(std::string::npos != pos) 
        { 
			stringStringToReplace.replace(pos, intLengthSearch, stringReplaceString);
			pos = stringStringToReplace.find(stringSearchString, pos + intLengthSearch);
        } 
		
        return stringStringToReplace; 
	} 
	
	
	
	
	std::vector<ImageSegment> SegmentOracle::getSegments(){
		return _mySegments;
	}


	void SegmentOracle::loadImage(){
		
#ifdef USE_TIFFS
		
		std::string myTiffInputName = ReplaceString(".png", ".tif", _myKlausImage->filename);
		
		_myOriginalImage.loadImage(_myInputPathName+ "/" + myTiffInputName);
		
		
#else

		_myOriginalImage.loadImage(_myInputPathName+ "/" + _myKlausImage->filename);

#endif
		_myOriginalImage.setImageType(OF_IMAGE_COLOR);
		
		/* Conver ofImage to ofCV type */
		_myCvImage.allocate(_myOriginalImage.width, _myOriginalImage.height);
		_myCvImage.setFromPixels(_myOriginalImage.getPixels(), _myOriginalImage.width, _myOriginalImage.height);
		
	}
	
	void SegmentOracle::resetBounds(){
		_myMaxX = std::numeric_limits<float>::min();
		_myMaxY = std::numeric_limits<float>::min();
		
		_myMinX = std::numeric_limits<float>::max();
		_myMinY = std::numeric_limits<float>::max();	
	}
	
	
	void SegmentOracle::resetCounts(){

		_myLineCount = 0;
		
		_myBorderLength = 0;
		_myPathLength = 0;
		
		
		for(int i = 0; i < _myVPs.size(); i++){
			VanishingPoint* myVP = &_myVPs.at(i);
			myVP->supportLength = 0;
			
		}
	}
	
	void SegmentOracle::addVanishingPoint(VanishingPoint theVP){
		_myVPs.push_back(theVP);
	}

	
	void SegmentOracle::updateBounds(float theX, float theY){
		_myMinX = MIN(_myMinX, theX);
		_myMinY = MIN(_myMinY, theY);
		
		_myMaxX = MAX(_myMaxX, theX);
		_myMaxY = MAX(_myMaxY, theY);
	}	
	
	void SegmentOracle::threadedFunction(){
		printf("#### Loading.\n");
		loadImage();
		
		if(_myOriginalImage.width > 10){ // image loade
			
			
			for(int i = 0; i < _myMaxSegments; i++){
				
				ostringstream myTemp;
				myTemp << _mySvgPathName << "/" << _myKlausImage->filename << "." << i << ".svg";

				_myPaths.clear();
				resetBounds();
				resetCounts();
				
				if(fileExists(myTemp.str())){
					std::cout << "#### Parsing " << myTemp.str() << " \n";

					parseSvg(myTemp.str());
					
					ostringstream myTempOut;
					myTempOut << _myOutputPathName << "/" << _myKlausImage->filename << "-" << _myOutputSuffix << "." << i << ".png";
					
					
					ostringstream myTempOutBase;
					myTempOutBase << _myKlausImage->filename  << "-" << _myOutputSuffix << "." << i << ".png";

					
					saveSegment(myTempOutBase.str(), myTempOut.str());

				} else {
					
					std::cout << "---- " << myTemp.str() << " does not exist \n";
					break;
				}
				
			}
			
		} else {
			std::cout << "!!!! Couldn't load image file " << _myKlausImage->filename << "\n";
		}
		

		//filterLines();
		
		
		while( _myIsFinishedCompletely == false ){
			if( lock() ){
				_myIsFinishedCompletely = true;
				
				unlock();
				
			}
		}
		
		
	}

	
	KlausImage* SegmentOracle::getKlausImage(){
		return _myKlausImage;
	}

	
	void SegmentOracle::start(){
		startThread(true, false);   // blocking, verbose
	}
	
	
	void SegmentOracle::kill(){
		stopThread();
	}
	
	
	SegmentOracle::~SegmentOracle(){
		printf("### SEGMOR destructor called");
		delete _myKlausImage;
	}
	
	
	void SegmentOracle::update(){
		
	}
	
	
	void SegmentOracle::draw(){
		ofSetColor(255,255,255);
		
		
		
		
		
	}
	
	
	
	void SegmentOracle::countLine(float theX1, float theY1, float theX2, float theY2){
		
		int vpIndex = -1;
		
		
		_myLineCount++;
		
		
		/* Scale coordinates up so they fit the original coordinate system in database */
#ifdef USE_TIFFS
		Segment mySegment(theX1 , theY1 , theX2 , theY2 );
#else
		Segment mySegment(theX1 * 2, theY1 * 2, theX2 * 2, theY2 * 2);
#endif
		
		
		float myLength = mySegment.magnitude();
		
		
		
		/* Count support for all Vanishing Points */
		for(int i = 0; i < _myVPs.size(); i++){
			VanishingPoint* myVP = &(_myVPs.at(i));
			
			
			
			if(mySegment.squaredDistanceToPoint(myVP) < _myLineThresholdSquared ){
				myVP->supportLength += myLength;
			}
		}
		
		
		/* Evaluate image width & height */
		int myWidth = _myOriginalImage.getWidth();
		int myHeight = _myOriginalImage.getHeight();
		
		
		
#ifdef USE_TIFFS
		
#else
		myWidth *= 2;
		myHeight *= 2;
#endif

		
		
		/* Count border touches */
		if( (mySegment.x1 < _myBorderThreshold && mySegment.x2 < _myBorderThreshold ) || 
			(mySegment.x1 > myWidth - _myBorderThreshold && mySegment.x2 > myWidth - _myBorderThreshold ) || 
			(mySegment.y1 < _myBorderThreshold && mySegment.y2 < _myBorderThreshold ) || 
			(mySegment.y1 > myHeight - _myBorderThreshold && mySegment.y2 > myHeight - _myBorderThreshold )
		   )
		{
			
			
			//printf("Image is %d x %d. %d ", myWidth, myHeight, _myBorderCount);
			
			_myBorderLength += myLength;
		}
		
		
		
		_myPathLength += myLength;
	}
	
	
	
	
	void SegmentOracle::parsePath(SugPathElement* thePath)
	{
		
		
		float scaleX =  1 * 0.1 ; 
		float scaleY = -1 * 0.1 ;
		
#ifdef USE_TIFFS
		scaleX *= 2;
		scaleY *= 2;
#endif
		
		float transX = 0;
		
		float transY = _myOriginalImage.height;

		
		float myCurrX = 0.0;
		float myCurrY = 0.0;
		
		float myPrevX = 0.0;
		float myPrevY = 0.0;
		
		float myFirstX = 0.0;
		float myFirstY = 0.0;
		
		float myLastCpX = 0.0;
		float myLastCpY = 0.0;
		
		float myPercentage;
		
		SugPathSeg* mySegment;
		
		
		vector<SugPathSeg*> myPathCommands;
		SugPathSegCurvetoCubic* myCubicSegment;
		vector<SugPathSeg*>::iterator myCommand;
		
		PolygonPath* myPath = NULL;
		//	outline = NULL;
		
		if (thePath->getPathCommandsAttribute("d", myPathCommands))
		{
			myCommand = myPathCommands.begin();
			while (myCommand != myPathCommands.end())
			{
				mySegment = (SugPathSeg*)(*myCommand);
				switch (mySegment->getPathSegType())
				{
					case PATHSEG_MOVETO_ABS:
						myPrevX = myCurrX;
						myPrevY = myCurrY;
						
						myCurrX = mySegment->getX() * scaleX + transX;
						myCurrY = mySegment->getY() * scaleY + transY;
						
						myFirstX = myCurrX;
						myFirstY = myCurrY;
						
						/* Save old path */
						if(myPath){
							_myPaths.push_back(*myPath);
						}
						
						delete myPath;
						myPath = NULL;
						
						/* A new path */
						myPath = new PolygonPath();
						//outline->setOpen();
						myPath->addPoint(myCurrX, myCurrY);
						updateBounds(myCurrX, myCurrY);
						
						
						
						break;
						
					case PATHSEG_MOVETO_REL:
						myPrevX = myCurrX;
						myPrevY = myCurrY;
						
						myCurrX += mySegment->getX() * scaleX;
						myCurrY += mySegment->getY() * scaleY;
						
						myFirstX = myCurrX;
						myFirstY = myCurrY;
						
						
						/* When using the potrace tracer, this means that this is a compund path and we are about to experience a hole ! */
						
						/* Save old path */
						if(myPath){
							_myPaths.push_back(*myPath);
						}
						
						delete myPath;
						myPath = NULL;
						
						/* A new path */
						myPath = new PolygonPath();
						//outline->setOpen();
						myPath->addPoint(myCurrX, myCurrY);
						updateBounds(myCurrX, myCurrY);
						
						break;
						
					case PATHSEG_LINETO_REL:
						myPrevX = myCurrX;
						myPrevY = myCurrY;
						
						myCurrX += mySegment->getX() * scaleX;
						myCurrY += mySegment->getY() * scaleY;
						
						
						
						countLine(myPrevX, myPrevY, myCurrX, myCurrY);
						
						if (myPath){
							myPath->addPoint(myCurrX, myCurrY);
							updateBounds(myCurrX, myCurrY);
						}
						
						break;
						
					case PATHSEG_CLOSEPATH:
						myPrevX = myCurrX;
						myPrevY = myCurrY;
						
						myCurrX = myFirstX;
						myCurrY = myFirstY;
						
						countLine(myPrevX, myPrevY, myCurrX, myCurrY);
						
						/* Save old path */
						if(myPath){
							_myPaths.push_back(*myPath);
						}
						
						delete myPath;
						myPath = NULL;
						
						break;
						
					case PATHSEG_CURVETO_CUBIC_REL:
						myCubicSegment = (SugPathSegCurvetoCubic*)mySegment;
						
						myLastCpX = myCurrX + myCubicSegment->getX2() * scaleX ;
						myLastCpY = myCurrY + myCubicSegment->getY2() * scaleY ;
						
						myPrevX = myCurrX;
						myPrevY = myCurrY;
						
						myCurrX += mySegment->getX() * scaleX;
						myCurrY += mySegment->getY() * scaleY;
						
						countLine(myPrevX, myPrevY, myCurrX, myCurrY);
						
						float myBezierX1 = myPrevX + myCubicSegment->getX1() * scaleX;
						float myBezierY1 = myPrevY + myCubicSegment->getY1() * scaleY;
						float myBezierX2 = myPrevX + myCubicSegment->getX2() * scaleX;
						float myBezierY2 = myPrevY + myCubicSegment->getY2() * scaleY;
						
						float myXOld = myPrevX;
						float myYOld = myPrevY;
						
						/* Segment the bezier curve */
						for (myPercentage = 0.05; myPercentage < 1.01; myPercentage += 0.05)
						{
							float myX = bezierPoint(myPrevX, myBezierX1, myBezierX2, myCurrX, myPercentage);
							float myY = bezierPoint(myPrevY, myBezierY1, myBezierY2, myCurrY, myPercentage);
							
							if (myPath){
								myPath->addPoint(myX, myY);
								updateBounds(myCurrX, myCurrY);
								
							}
							
							countLine(myXOld, myYOld, myX, myY);
							
							myYOld = myY;
							myXOld = myX;
						}
						break;
						
						
				}
				myCommand++;
			}
		}
	}
	
	void SegmentOracle::parse(SugElement* theElement)
	{
		SugNode* myNode;
		
		switch (theElement->getElementType())
		{
			case SugTypes::E_SVG:
				break;
				
			case SugTypes::E_G:
				//std::cout << "GROUP\n";
				break;
				
			case SugTypes::E_PATH:
				//std::cout << "PATH OR SO\n";
				SugPathElement* myPath = (SugPathElement*)theElement;
				parsePath(myPath);
				
				break;
		}   
		
		/* Iterate all the way through the svg tree */	
		myNode = theElement->getFirstChild();
		if (myNode)
			parse((SugElement*)myNode);
		
		myNode = theElement->getNextSibling();
		if (myNode)
			parse((SugElement*)myNode);
	}
	
	
	
	
	void SegmentOracle::parseSvg(std::string theFileName){

		
		SugSvgElement myDocument;
				
		
		parseSvgToObject((char*)theFileName.c_str(), &myDocument);
				
		parse(&myDocument);

	}
	
	
	
	bool SegmentOracle::hasFinishedCompletely(){
		return _myIsFinishedCompletely;
	}
	
		
	bool SegmentOracle::fileExists(std::string theFileName){
		bool flag = false;
		fstream fin;
		fin.open(theFileName.c_str() ,ios::in);
		if( fin.is_open() )
		{
			cout<<"file exists"<<endl;
			flag=true;
		}
		fin.close();
		
		return flag;
	}
	
	
	

	
	
	
	float SegmentOracle::bezierPoint(float a, float b, float c, float d, float t) {
		float t1 = 1.0f - t;
		return a*t1*t1*t1 + 3*b*t*t1*t1 + 3*c*t*t*t1 + d*t*t*t;
	}
	
	
	
}